﻿Ext.Template = Ext.extend(Object, {
    constructor: function (html) {
        var me = this,
            args = arguments,
            buffer = [],
            value, i, length;

        me.initialConfig = {};

        if (Ext.isArray(html)) {//如果html是数组，则把它变为字符串
            html = html.join("");
        }
        else if (args.length > 1) {//否则如果有多于一个参数
            for (i = 0, length = args.length; i < length; i++) {
                value = args[i];
                if (typeof value == 'object') {//如果当前参数是一个对象
                    Ext.apply(me.initialConfig, value);
                    Ext.apply(me, value);
                } else {
                    buffer.push(value);//所有参数都放入数组中
                }
            }
            html = buffer.join('');//所有参数都转换成一个字符串
        }


        me.html = html;

        if (me.compiled) {
            me.compile();//编译模板
        }
    },
    isTemplate: true,

    disableFormats: false,

    re: /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,

    //Returns an HTML fragment of this template with the specified values applied.
    applyTemplate: function (values) {
        var me = this,
            useFormat = me.disableFormats !== true,
            fm = Ext.util.Format,
            tpl = me;

        if (me.compiled) {
            return me.compiled(values);
        }
        function fn(m, name, format, args) {
            if (format && useFormat) {
                if (args) {
                    args = [values[name]].concat(new Function('return [' + args + '];')());
                } else {
                    args = [values[name]];
                }
                if (format.substr(0, 5) == "this.") {
                    return tpl[format.substr(5)].apply(tpl, args);
                }
                else {
                    return fm[format].apply(fm, args);
                }
            }
            else {
                return values[name] !== undefined ? values[name] : "";
            }
        }
        return me.html.replace(me.re, fn);
    },


    set: function (html, compile) {
        var me = this;
        me.html = html;
        me.compiled = null;
        return compile ? me.compile() : me;
    },

    compile: function () {
        var me = this,
            fm = Ext.util.Format,
            useFormat = me.disableFormats !== true,
            body, bodyReturn;

        function fn(m, name, format, args) {
            if (format && useFormat) {
                args = args ? ',' + args : "";
                if (format.substr(0, 5) != "this.") {
                    format = "fm." + format + '(';
                }
                else {
                    format = 'this.' + format.substr(5) + '(';
                }
            }
            else {
                args = '';
                format = "(values['" + name + "'] == undefined ? '' : ";
            }
            return "'," + format + "values['" + name + "']" + args + ") ,'";
        }

        bodyReturn = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn);
        body = "this.compiled = function(values){ return ['" + bodyReturn + "'].join('');};";
        eval(body);
        return me;
    },

    //Applies the supplied values to the template and inserts the new node(s) as the first child of el.
    insertFirst: function (el, values, returnElement) {
        return this.doInsert('afterBegin', el, values, returnElement);
    },

    //Applies the supplied values to the template and inserts the new node(s) before el.
    insertBefore: function (el, values, returnElement) {
        return this.doInsert('beforeBegin', el, values, returnElement);
    },

    //Applies the supplied values to the template and inserts the new node(s) after el.
    insertAfter: function (el, values, returnElement) {
        return this.doInsert('afterEnd', el, values, returnElement);
    },

    //Applies(适用于) the supplied(提供) values to the template and appends(追加) the new node(s) to the specified el.
    //以追加的形式渲染模板，el是容器模板，values是数据，returnElement为true时返回一个Element对象
    append: function (el, values, returnElement) {
        return this.doInsert('beforeEnd', el, values, returnElement);
    },

    //
    doInsert: function (where, el, values, returnEl) {
        el = Ext.getDom(el);
        var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
        return returnEl ? Ext.get(newNode, true) : newNode;
    },

    overwrite: function (el, values, returnElement) {
        el = Ext.getDom(el);
        el.innerHTML = this.applyTemplate(values);
        return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
    }
});

Ext.Template.from = function (el, config) {
    el = Ext.getDom(el);
    return new Ext.Template(el.value || el.innerHTML, config || '');
};

